package types

import (
	"net/http"
	"encoding/json"
	"github.com/valyala/fasthttp"
	"strings"
	"net/url"
	"encoding/base64"
	"errors"
	"github.com/funlake/gopkg/utils/log"
	"gateway/conf"
	"github.com/funlake/gopkg/utils"
	"gateway/lib"
	"bytes"
)
type Appjson struct{
	Id string `json:"id"`
	Appid string `json:"appid"`
	AppSecret string `json:"secret"`
}
func NewProxyContext(ctx *fasthttp.RequestCtx) (*ProxyContext,error){
	//paths := strings.SplitN (utils.ByteToStr(ctx.Path()),"/",3)[1:]
	//vhost := paths[0]
	//service := ""
	//mapkey := vhost
	//
	//if len(paths) > 1{
	//	service = paths[1]
	//	mapkey = mapkey + "/" + paths[1]
	//}
	//isset,upstream := getUpstream(vhost,service)
	//log.Warning(vhost,service)
	//if !isset {
	//	return nil,errors.New("路由未注册")
	//}
	//log.Warning("%d",maxRequestCount)
	//log.Warning(mapkey)
	//vhost, service = parseUpstream(mapkey)

	domain,_ := conf.GetConfig().Get("app").GetString("domain","")
	//newRequest := &fasthttp.Request{}
	//ctx.Request.CopyTo(newRequest)
	newRequest := ctx.Request
	proxyContext := &ProxyContext{
		utils.ByteToStr(newRequest.URI().Scheme()),
		utils.ByteToStr(newRequest.Header.Method()),
		domain,
		utils.ByteToStr(newRequest.RequestURI()),
		newRequest.Body(),
		newRequest.Header,
		"",
		"",
		"",
		nil,
	}
	return proxyContext,nil
}
type ProxyContext struct {
	Scheme      string
	Method      string
	Domain      string
	QueryString string
	PostData    []byte
	Header      fasthttp.RequestHeader
	Vhost       string
	Service     string
	Mapkey      string
	Req 		*http.Request
}

func(this *ProxyContext) GetToken() string{
	acctoken,ok := ParseHeaderAccessToken(this.Header)
	if !ok {
		acctoken = ParseAccessToken(string(this.PostData))
	}
	if acctoken == "" {
		acctoken = ParseAccessToken(this.QueryString)
	}
	return acctoken
}
func (this ProxyContext) GetSign() string{
	return string(this.Header.Peek("CB-SIGN"))
}
func (this *ProxyContext) GetAppInfo() (*Appjson,error){
	acctoken := this.GetToken()
	if acctoken == ""{
		return nil,errors.New("access_token is empty")
	}
	cache := lib.NewCache()
	app,_ := cache.GetAppInfo(acctoken)
	appjson := &Appjson{}
	err:= json.Unmarshal(utils.StrToByte(app),appjson)
	if err == nil {
		return appjson , nil
	}else{
		log.Error(err.Error())
	}
	appjson.Appid = ""
	return nil,err
}

func ParseAccessToken(q string)  string{
	if strings.Contains(q,"?") {
		qs := strings.Split(q,"?")
		q   = qs[1]
	}
	qs,err := url.ParseQuery(q)
	if err!=nil {
		log.Warning("%s",err.Error())
	}
	return qs.Get("access_token")
}

func ParseHeaderAccessToken(header fasthttp.RequestHeader) (acctoken string,ok bool)  {
	auth := header.Peek("Authorization")
	return parseBearerAuth(string(auth))
}

func parseBearerAuth(auth string) (acctoken string, ok bool) {
	const prefix = "Bearer "
	if !strings.HasPrefix(auth, prefix) {
		return "",false
	}
	//log.Info("token base64 encode from header:"+auth[len(prefix):])
	c, err := base64.StdEncoding.DecodeString(auth[len(prefix):])
	//log.Info("token base64 decode from header:",string(c))
	if err != nil {
		return "",false
	}
	return string(c),true
}
func (this *ProxyContext) GetJwtIss() string{
	auth := this.Header.Peek("Authorization")
	if auth == nil{
		return "all"
	}
	jwt := bytes.Split(auth,[]byte("."))
	return string(jwt[1])
}
func (this *ProxyContext) GetLoginToken() string{
	token := ParseParamByKey(string(this.PostData),"token")
	if token == "" {
		token = ParseParamByKey(this.QueryString,"token")
	}
	return token
}

func ParseParamByKey(q string,key string)  string{
	if strings.Contains(q,"?") {
		qs := strings.Split(q,"?")
		q   = qs[1]
	}
	qs,err := url.ParseQuery(q)
	if err!=nil {
		log.Warning("%s",err.Error())
	}
	return qs.Get(key)
}

func getUpstream(vhost string,service string) (bool,string){
	cache := lib.NewCache()
	vk := vhost
	if service!="" {
		vk = vk + "/" + service
	}
	upstream,error := cache.GetUpstream(vk)
	if upstream == ""{
		return false ,""
	}
	//beego.Warning(vk,upstream)
	if error != nil {
		return false,""
	}
	return true,upstream
}

func parseUpstream(upstream string) (vhost string,service string){
	vs := strings.SplitN(upstream,"/",2)
	path := ""
	if len(vs) > 1 {
		path = vs[1]
	}
	return strings.TrimRight(vs[0], "/"), path
}

type stats struct{
	requests int64
	rejects int64
	fails int64
}